import { useAppDispatch } from '@/store/hooks'
import  {login, sendCode } from '@/store/module/login'
import { Button, Form, Input, InputRef, NavBar, Toast } from 'antd-mobile'
import React, { useEffect, useRef, useState } from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import styles from './index.module.scss'


/**
 * 验证码倒计时
 */
const useTimeLeft = () => {
  // 准备倒计时状态
  const [timeLeft, setTimeLeft] = useState(0)
  // 记录定时器id的ref对象
  const timerIdRef = useRef(-1)

  // 监听定时器的改变，在 timeLeft 值为 0 时，清理定时器
  useEffect(() => {
    if (timeLeft === 0) {
      // console.log('清理定时器')
      clearInterval(timerIdRef.current)
    }
  }, [timeLeft])

  // 组件卸载时，清理定时器
  useEffect(() => {
    return () => {
      clearInterval(timerIdRef.current)
    }
  }, [])

  // 由于这段代码不是立即执行，而是在点击发送验证码的事件处理程序中调用的
  // 所以，此处，我能把它封装到一个函数中，什么时候用，什么时候来调用这个函数即可
  const startInterval = () => {
    // 只要开启倒计时，就是从 60 秒开始倒计时
    setTimeLeft(60)
    // window.setInterval 表示使用 浏览器 中的定时器
    timerIdRef.current = window.setInterval(() => {
      // console.log('定时器代码执行')
      setTimeLeft(prev => prev - 1)
    }, 1000)
  }

  // 组件中需要什么数据，此处，就返回什么数据即可
  return {
    timeLeft,
    startInterval,
  }
}

/**
 * 发送验证码的相关逻辑
 * @returns 
 */
const useSendCode = () => {
  const dispatch = useAppDispatch()
  // 创建操作 Input 组件的 ref 对象
  const inputRef = useRef<InputRef>(null)
  // 创建 form 组件的实例
  const [form] = Form.useForm()
  // 调用自定义 hook
  const { timeLeft, startInterval } = useTimeLeft()
  // 发送验证码
  const onSendCode = () => {
    // form.getFieldValue('mobile') 获取表单中某个表单项的值
    // console.log('onSendCode', form.getFieldValue('mobile'))
    const hasError = form.getFieldError('mobile').length > 0
    const mobile = (form.getFieldValue('mobile') ?? '') as string
    if (hasError || mobile.trim() === '') {
      return inputRef.current?.focus()
    }

    // 发送验证码
    dispatch(sendCode(mobile))

    // 开启定时器的倒计时
    startInterval()
  }
  return {
    form,
    timeLeft,
    onSendCode,
    inputRef,
  }
}


// 使用自定义 hook 来处理跟表单提交相关的状态逻辑代码
const useFinish = () => {
  const navigate = useNavigate()
  const dispatch = useAppDispatch()
  const location = useLocation()

  const locationState = location.state as null | { from: string }

  // 表单提交时触发
  const onFinish = async (values: { mobile: string; code: string }) => {
    try {
      // 注意：此处，需要调用 unwrap() 才能够正常捕获 thunk action 中的错误
      await dispatch(login(values)).unwrap()
      Toast.show({
        content: '登录成功',
        duration: 800,
        afterClose: () => {
          navigate(locationState?.from ?? '/home', { replace: true })
        },
      })
    } catch (e) {
      const error = e as { data: null; message: string }
      // 错误处理
      Toast.show({
        content: error.message,
        duration: 1000,
      })
    }
  }

  return onFinish
}

const Login = () => {
  const { form, inputRef, timeLeft, onSendCode } = useSendCode()
  // 调用自定义 hook
  const onFinish = useFinish()
  return (
    <div className={styles.root}>
      <NavBar></NavBar>
      <div className="login-form">
        <h3 className="title">账号登录</h3>
        {/* 登录表单组件 */}
        <Form
          // 将 表单实例 form 与该 Form 组件关联到一块
          // 关联后，就可以通过 form 来使用 Form 组件中的方法了
          form={form}
          // 设置表单初始值
          initialValues={
            {
              // mobile: '1386666888',
              // code: '24681',
            }
          }
          onFinish={onFinish}
        >
          {/* Form 中的每一项是一个 Form.Item 组件 */}
          <Form.Item
            className="login-item"
            name="mobile"
            rules={[
              // 必填
              {
                required: true,
                message: '请输入手机号',
              },
              // 手机号码格式
              {
                pattern: /^1[3-9]\d{9}$/,
                message: '手机号格式错误',
              },
            ]}
            // 表单校验的触发时机，默认（onChange）会在输入内容时进行校验
            // onBlur 表示： 在文本框失焦点时，再进行校验
            validateTrigger={['onChange']}
          >
            {/* 文本框组件 */}
            <Input placeholder="请输入手机号" maxLength={11} ref={inputRef} />
          </Form.Item>

          <Form.Item
            className="login-item"
            // extra 表示额外的信息，位于表单项的右侧
            extra={
              <span
                className="code-extra"
                onClick={timeLeft === 0 ? onSendCode : undefined}
              >
                {timeLeft === 0 ? '发送验证码' : `${timeLeft}s后重新获取`}
              </span>
            }
            name="code"
            rules={[
              // 必填项
              { required: true, message: '请输入验证码' },
              // 长度校验
              { len: 6, message: '验证码应为6位' },
            ]}
            validateTrigger={['onChange']}
          >
            {/* autoComplete="off" 用来关闭浏览器默认的自动完成（历史输入内容的提示）功能 */}
            <Input
              placeholder="请输入验证码"
              autoComplete="off"
              maxLength={6}
            />
          </Form.Item>

          {/* noStyle 表示不提供 Form.Item 自带的样式 */}
          {/* 
            设置 shouldUpdate 属性后，表单中任意表单项值的改变，都会重新执行此处的回调函数
            这样，就可以打到实时判断登录按钮是否启用或禁用的目的了
          */}
          <Form.Item noStyle shouldUpdate>
            {() => {
              // console.log('登录按钮重新渲染了', form.isFieldsTouched(true))
              // console.log(
              //   '登录按钮重新渲染了',
              //   form.getFieldsError().filter(item => item.errors.length !== 0)
              //     .length !== 0
              // )
              // form.isFieldsTouched(true)
              //  作用： 检查所有表单项的值是否都被操作过，如果被操作过，值为：true；否则为，false

              // form.getFieldsError()
              //  作用： 获取表单项中所有的错误信息
              //  form.getFieldsError().filter(item => item.errors.length !== 0)
              //    作用： 过滤出一个数组，数组中只包含有错误的表单项
              return (
                <Button
                  block
                  type="submit"
                  color="primary"
                  className="login-submit"
                  disabled={
                    // !form.isFieldsTouched(true)
                    //  表示： 如果所有表单项都操作过，这个表达式的结果为： false（ 因为取反了 ），也就表示不禁用，也就是启用
                    //        否则，结果为： true
                    // 简单来说，就是： 当所有的表单项都操作过，这个按钮才是启用的

                    /*
                      !!form
                        .getFieldsError()
                        .filter(({ errors }) => errors.length).length
                      
                      表示：获取表单项中所有错误的表单项的长度，并且转化为 布尔值
                           只要表单项中有错误项（校验失败），此时，值为 true，也就是 disabled 值为 true，按钮就是禁用的；否则，按钮就是启用的
                      简单来说，就是： 当表单项中没有错误，按钮就是启用的；否则，就是禁用
                    */
                    !form.isFieldsTouched(true) ||
                    !!form
                      .getFieldsError()
                      .filter(({ errors }) => errors.length).length
                  }
                >
                  登 录
                </Button>
              )
            }}
          </Form.Item>
        </Form>
      </div>
    </div>
  )
}

export default Login